﻿
Imports System.IO

Public Module pac_trans
    Private ReadOnly PAC_TRANS_SEND_PACKAGE_SIZE As Int64
    Private ReadOnly PAC_TRANS_SEND_QPS As Int64
    Private ReadOnly PAC_TRANS_SEND_LATENCY_MS As Int64
    Private ReadOnly PAC_TRANS_RECEIVE_PACKAGE_SIZE As Int64
    Private ReadOnly PAC_TRANS_RECEIVE_QPS As Int64
    Private ReadOnly PAC_TRANS_RECEIVE_LATENCY_MS As Int64
    Private ReadOnly checksum() As Byte = Nothing
    Private ReadOnly pach_len As Byte = 0
    Private ReadOnly len_len As Byte = 0

    Sub New()
        checksum = +convertor.tobytes(checksum_64)
        len_len = sizeofInt64
        pach_len = checksum.Length() + len_len
        PAC_TRANS_SEND_PACKAGE_SIZE = counter.registerAverageAndLastAverage("PAC_TRANS_SEND_PACKAGE_SIZE")
        PAC_TRANS_SEND_QPS = counter.registerRateAndLastRate("PAC_TRANS_SEND_QPS")
        PAC_TRANS_SEND_LATENCY_MS = counter.registerAverageAndLastAverage("PAC_TRANS_SEND_LATENCY_MS")
        PAC_TRANS_RECEIVE_PACKAGE_SIZE = counter.registerAverageAndLastAverage("PAC_TRANS_RECEIVE_PACKAGE_SIZE")
        PAC_TRANS_RECEIVE_QPS = counter.registerRateAndLastRate("PAC_TRANS_RECEIVE_QPS")
        PAC_TRANS_RECEIVE_LATENCY_MS = counter.registerAverageAndLastAverage("PAC_TRANS_RECEIVE_LATENCY_MS")
    End Sub

    Private Function create_pac_head(ByVal l As Int64) As Byte()
        Dim rtn() As Byte = Nothing
        ReDim rtn(pach_len - 1)
        memcpy(rtn, 0, checksum, 0, checksum.Length())
        memcpy(rtn, checksum.Length(), +convertor.tobytes(l), 0, len_len)
        Return rtn
    End Function

    Private Function parse_pac_head(ByVal pach As bytes, ByRef len As Int64) As Boolean
        If pach.size() = pach_len AndAlso
           memcmp(+pach, checksum, checksum.Length()) = 0 Then
            Dim len_b As bytes = Nothing
            len_b = New bytes()
            len_b.size() = len_len
            memcpy(+len_b, 0, +pach, checksum.Length(), len_len)
            Return convertor.toint64(len_b, len) AndAlso len > 0
        Else
            Return False
        End If
    End Function

    's, the stream to write the data into
    'd, the data
    'ec, the eventComb for return
    'return value,  true if the initial check has been passed,
    '               false if stream is not writeable / d is nothing, etc
    'ec.end_result() shows the result of the operation,
    'if it's false, shows the stream may not be able to operate later
    Public Function send(ByVal s As Stream,
                         ByVal d() As Byte,
                         ByVal holdtimeMs As UInt32,
                         ByRef ec As eventComb) As Boolean
        If d Is Nothing Then
            Return False
        ElseIf d.Length() = 0 Then
            ec = eventComb.succeeded()
            Return True
        Else
            Dim pach() As Byte = Nothing
            pach = create_pac_head(d.LongLength())
            Dim pachec As eventComb = Nothing
            Dim dataec As eventComb = Nothing
            If writestream4(s, pach, ec:=pachec) AndAlso
               writestream4(s, d, ec:=dataec) Then
                Dim wec As eventComb = Nothing
                Dim startMs As Int64 = 0
                wec = New eventComb(Function() As Boolean
                                        startMs = Now.milliseconds()
                                        Return waitfor(pachec) AndAlso goto_next()
                                    End Function,
                                    Function() As Boolean
                                        If pachec.end_result() Then
                                            Return waitfor(dataec) AndAlso goto_next()
                                        Else
                                            Return False
                                        End If
                                    End Function,
                                    Function() As Boolean
                                        If dataec.end_result() Then
                                            counter.increase(PAC_TRANS_SEND_PACKAGE_SIZE, pach_len + d.LongLength())
                                            counter.increase(PAC_TRANS_SEND_QPS)
                                            counter.increase(PAC_TRANS_SEND_LATENCY_MS, Now.milliseconds() - startMs)
                                            Return goto_end()
                                        Else
                                            Return False
                                        End If
                                    End Function)
                'the wec will always work until finish or link lost, to avoid partial data transport
                ec = New eventComb(Function() As Boolean
                                       Return waitfor(wec, holdtimeMs) AndAlso goto_next()
                                   End Function,
                                   Function() As Boolean
                                       Return wec.end_result() AndAlso goto_end()
                                   End Function)
                Return True
            Else
                Return False
            End If
        End If
    End Function

    Public Function send(ByVal s As Stream,
                         ByVal d() As Byte,
                         ByRef ec As eventComb) As Boolean
        Return send(s, d, maxUInt32, ec)
    End Function

    Public Function receive(ByVal s As Stream,
                            ByVal d As pointer(Of bytes),
                            ByVal holdtimeMs As UInt32,
                            ByRef ec As eventComb) As Boolean
        Dim pachec As eventComb = Nothing
        Dim pach As pointer(Of bytes) = Nothing
        pach = New pointer(Of bytes)()
        If readstream4(s, length:=pach_len, ec:=pachec, result:=pach) Then
            Dim dataec As eventComb = Nothing
            Dim rec As eventComb = Nothing
            Dim len As Int64 = 0
            Dim startMs As Int64 = 0
            rec = New eventComb(Function() As Boolean
                                    startMs = Now.milliseconds()
                                    Return waitfor(pachec) AndAlso goto_next()
                                End Function,
                                Function() As Boolean
                                    Return pachec.end_result() AndAlso
                                           parse_pac_head(+pach, len) AndAlso
                                           readstream4(s, length:=len, ec:=dataec, result:=d) AndAlso
                                           waitfor(dataec) AndAlso
                                           goto_next()
                                End Function,
                                Function() As Boolean
                                    If dataec.end_result() Then
                                        counter.increase(PAC_TRANS_RECEIVE_PACKAGE_SIZE, pach_len + len)
                                        counter.increase(PAC_TRANS_RECEIVE_QPS)
                                        counter.increase(PAC_TRANS_RECEIVE_LATENCY_MS, Now.milliseconds() - startMs)
                                        Return goto_end()
                                    Else
                                        Return False
                                    End If
                                End Function)
            ec = New eventComb(Function() As Boolean
                                   Return waitfor(rec, holdtimeMs) AndAlso goto_next()
                               End Function,
                               Function() As Boolean
                                   Return rec.end_result() AndAlso goto_end()
                               End Function)
            Return True
        Else
            Return False
        End If
    End Function

    Public Function receive(ByVal s As Stream,
                            ByVal d As pointer(Of bytes),
                            ByRef ec As eventComb) As Boolean
        Return receive(s, d, maxUInt32, ec)
    End Function
End Module
